fix: adopt canonical hardened automerge template - #19
Conversation
The workflow called gh pr merge --auto with nothing gating it, and this repo has DEPENDABOT_AUTOMERGE_TOKEN provisioned, so minor/patch bumps were auto-merging with nothing waiting on ci_validate_jekyll.yml. Replaced with the canonical template from vln-devsecops/guidance, which gates the merge on the PR head commit's own check runs.
There was a problem hiding this comment.
Pull request overview
Hardens Dependabot auto-merge by replacing the existing workflow with a canonical template that gates merges on the PR head commit’s completed check runs, rather than relying on branch protection / required checks.
Changes:
- Reworks the Dependabot auto-merge workflow to wait for (and validate) sibling check runs on the PR head SHA before approving/merging.
- Makes auto-merge explicitly opt-in via
DEPENDABOT_AUTOMERGE_TOKEN, with clear notice behavior when not provisioned. - Adds job-level safeguards and documentation comments for
pull_request_targetsecret-handling and self-exclusion while polling checks.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| checks: read |
There was a problem hiding this comment.
Right, and fixed at the source: guidance#15 drops GITHUB_TOKEN to contents: read / pull-requests: read / checks: read.
Every write this workflow performs — the approve and the merge — goes through DEPENDABOT_AUTOMERGE_TOKEN, so the write scope bought nothing and, under pull_request_target, was pure blast radius: a compromised step or third-party action would have held a repo-write token. The suite now fails if any write scope is reintroduced.
This repo picks it up on the next resync from the canonical template.
| echo "No sibling check runs appeared within ${settle_seconds}s; nothing to gate on." | ||
| exit 0 |
There was a problem hiding this comment.
Accurate, and the wording is mine to fix — the PR description overstates it.
The behaviour is deliberate: exiting 0 when no sibling checks appear is what lets a repo with no PR-triggered CI merge at all. But you are right that "fails closed" is then wrong as written, and the honest description is that it fails closed on any check it can see, and merges when it can see none.
Whether that should become a hard failure is a genuine open question now, because the standing rule changed: auto-merge is only supposed to be added once a repo has at least one check to gate on (guidance#14). Under that rule, a repo reaching this path is misconfigured, and failing closed would surface it rather than silently merging.
I have put it to the operator rather than changing it unilaterally — flipping it would stop merges in any repo that legitimately has no CI yet, across ~46 repos.
| runs-on: ubuntu-latest | ||
| if: github.actor == 'dependabot[bot]' |
There was a problem hiding this comment.
Correct, and the sharpest point in the review — but I have deliberately not changed it, because it is a policy call rather than a pure bug fix. Raised with the operator.
The bug is real. github.actor is whoever triggered the event, not the PR author. On reopened, or when a maintainer pushes to the branch, github.actor is the human, the if evaluates false, and the job silently no-ops on a genuine Dependabot PR. Nothing reports that — it just never merges.
Why it is not a drive-by fix. Switching to github.event.pull_request.user.login also widens when auto-merge fires. Today, a human touching a Dependabot branch effectively opts that PR out of auto-merge. Keyed on the PR author, those PRs would auto-merge again — including any human-authored commits pushed onto the Dependabot branch, which is a plausible way for unreviewed changes to reach the default branch under an auto-merge label.
Both behaviours are defensible; they are different policies. Since the template is copied into ~46 repos, I would rather have that decided than pick silently. Tracked alongside guidance#15.
This repo is auto-merging unguarded right now
.github/workflows/ci_dependabot_automerge.ymlcallsgh pr merge --auto --squashwith nothing gating it, andDEPENDABOT_AUTOMERGE_TOKENis provisioned here — so this is live, not latent. Minor/patch Dependabot PRs are merging without waiting forci_validate_jekyll.yml.gh pr merge --autodoes not mean "merge when CI passes"; it means "merge when the repository's merge requirements are satisfied".masterhas no branch protection and no required status check, so there are no requirements and it merges immediately. That is exactly how node-dashboard#37 merged past a failingplanjob.Fix
Verbatim copy of the canonical template from
guidance@main. It polls the PR head commit's own check runs and blocks the merge unless every other check completed successfully — so it fails closed regardless of branch-protection config or plan tier.ci_validate_jekyll.ymlgives it something real to wait on.Do not hand-edit it: the trigger, token handling, run-id self-exclusion and settle window are each load-bearing and each was a real failure elsewhere in the portfolio. See
runbooks/dependabot-automerge.md.Found during a cross-org scan on 2026-08-03 that extended to
vln-bookstore,rxmail-io,vln-sneakyimmediatetapewormandcpp4theselftaught— orgs not covered by earlier passes.